__all__ = ["Sequential", "Dispatch", "Branch"]


class Sequential(object):
    """
    A pipeline that executes a sequence of pipelines.

    This pipeline takes a value and passes it to each pipeline in order. The output of each pipeline is passed to the
    next one. Finally, the output of the last pipeline is returned.
    """

    def __init__(self, *pipelines):
        self.pipelines = pipelines

    def __call__(self, value=None):
        result = value
        for pipeline in self.pipelines:
            result = pipeline(result)
        return result


class Dispatch(object):
    """
    A pipeline that dispatches a value to a sequence of pipelines.

    This pipeline takes a value and passes it to each pipeline in order. The outputs of each pipeline, if not None,
    are returned as a tuple.
    """

    def __init__(self, *pipelines):
        self.pipelines = pipelines

    def __call__(self, value=None):
        results = []
        for pipeline in self.pipelines:
            result = pipeline(value)
            if result is not None:
                results.append(result)
        return tuple(results)


class Branch(object):
    """
    A pipeline that creates a branch.

    The pipelines in the branch are executed sequentially. However, the returned value will be ignored and the value
    passed in will be returned as-is.

    This is useful for marshalling intermediate stages without stopping the whole pipeline.
    """

    def __init__(self, *pipelines):
        self.branch = Sequential(*pipelines)

    def __call__(self, value=None):
        self.branch(value)
        return value
